<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>车辆召回大数据分析仪表盘</title>
    <script src="/static/js/chart.js"></script>
    <script src="/static/js/chartjs-adapter-date-fns.js"></script>
    <style>
        :root {
            --bg-color: #0c113b;
            --primary-color: rgba(22, 33, 62, 0.5);
            --border-color: rgba(36, 255, 236, 0.3);
            --glow-color: rgba(36, 255, 236, 0.5);
            --accent-color: #24ffec;
            --text-color: #e0e0e0;
            --font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
        }

        body {
            background-color: var(--bg-color);
            background-image: radial-gradient(circle, #1a1a2e 0%, #0c113b 100%);
            color: var(--text-color);
            font-family: var(--font-family);
            margin: 0;
            padding: 15px;
        }

        .container {
            display: flex;
            flex-direction: column;
            height: calc(100vh - 30px);
        }

        header {
            background: linear-gradient(90deg, transparent, rgba(36, 255, 236, 0.1), transparent);
            text-align: center;
            padding: 5px 0;
            border-bottom: 2px solid var(--border-color);
            position: relative;
        }

        header h1 {
            font-size: 2.2em;
            color: var(--accent-color);
            text-shadow: 0 0 10px var(--glow-color), 0 0 20px var(--glow-color);
            margin: 10px 0;
        }

        #live-clock {
            position: absolute;
            top: 50%;
            right: 20px;
            transform: translateY(-50%);
            font-size: 1.2em;
            color: var(--text-color);
            letter-spacing: 2px;
        }

        main {
            flex-grow: 1;
            padding: 15px 0;
            display: grid;
            grid-template-columns: repeat(12, 1fr);
            grid-template-rows: repeat(10, 1fr);
            gap: 15px;
            height: 100%;
        }

        .panel {
            background-color: var(--primary-color);
            border: 1px solid var(--border-color);
            border-radius: 8px;
            box-shadow: inset 0 0 15px rgba(36, 255, 236, 0.1), 0 0 10px rgba(12, 17, 59, 0.5);
            padding: 15px;
            display: flex;
            flex-direction: column;
        }
        
        .panel-title {
            font-size: 1.1em;
            font-weight: bold;
            color: var(--accent-color);
            margin: 0 0 10px 0;
            padding-bottom: 5px;
            border-bottom: 1px solid var(--border-color);
        }

        .panel .chart-content, .panel .kpi-content, .panel .table-content {
            flex-grow: 1;
            position: relative;
        }
        
        .panel canvas {
             max-height: 100%;
        }

        /* Grid layout assignments */
        #kpi-panel { grid-area: 1 / 1 / 3 / 4; }
        #manufacturer-panel { grid-area: 1 / 4 / 6 / 9; }
        #make-panel { grid-area: 1 / 9 / 6 / 13; }
        #type-panel { grid-area: 3 / 1 / 7 / 4; }
        #component-panel { grid-area: 7 / 1 / 11 / 4; }
        #initiative-panel { grid-area: 6 / 4 / 11 / 7; }
        #model-panel { grid-area: 6 / 7 / 11 / 13; }
        #yearly-trend-panel { grid-area: 11 / 1 / 11 / 13; display: none; } /* Hide for now */
        #monthly-trend-panel { grid-area: 11 / 1 / 11 / 13; display: none; } /* Hide for now */

        .kpi-value {
            font-size: 3.5em;
            font-weight: bold;
            color: var(--accent-color);
            text-align: center;
            margin: auto;
            display: flex;
            align-items: center;
            justify-content: center;
            height: 100%;
            text-shadow: 0 0 15px var(--glow-color);
        }
        
        .table-content {
             overflow-y: auto;
        }
        
        .data-table {
            width: 100%;
            border-collapse: collapse;
        }
        
        .data-table th, .data-table td {
            text-align: left;
            padding: 8px;
            border-bottom: 1px solid var(--border-color);
        }

        #refresh-btn {
            position: absolute;
            top: 50%;
            left: 20px;
            transform: translateY(-50%);
            background-color: var(--accent-color);
            color: #0c113b;
            border: none;
            padding: 8px 15px;
            font-size: 1em;
            font-weight: bold;
            border-radius: 5px;
            cursor: pointer;
            transition: all 0.3s ease;
        }
        #refresh-btn:hover { box-shadow: 0 0 15px var(--glow-color); }
        
        /* Modal and Progress styles are the same as before, no need to repeat fully */
        .modal { display: none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.7); }
        .modal-content { background-color: #0c113b; margin: 15% auto; padding: 25px; border: 1px solid var(--border-color); width: 60%; max-width: 800px; border-radius: 10px; box-shadow: 0 0 25px var(--glow-color); }
        #progress-log { height: 250px; background-color: rgba(0,0,0,0.3); border-radius: 5px; padding: 15px; overflow-y: auto; margin-top: 20px; font-family: 'Courier New', monospace; }
        .progress-bar-container { width: 100%; background-color: rgba(0,0,0,0.3); border-radius: 5px; overflow: hidden; margin-top: 10px; }
        #progress-bar { width: 0%; height: 25px; background-color: var(--accent-color); text-align: center; line-height: 25px; color: #0c113b; font-weight: bold; transition: width 0.4s ease; }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <button id="refresh-btn">刷新数据</button>
            <h1>车辆召回大数据分析仪表盘</h1>
            <div id="live-clock"></div>
        </header>

        <main class="dashboard-grid">
            <div class="panel" id="kpi-panel">
                <div class="panel-title">累计预估影响车辆</div>
                <div class="kpi-content">
                    <span id="kpi-total-units">0</span>
                </div>
            </div>
            <div class="panel" id="manufacturer-panel">
                <div class="panel-title">制造商召回数 Top 10</div>
                <div class="chart-content">
                    <canvas id="top10ManufacturersChart"></canvas>
                </div>
            </div>
            <div class="panel" id="make-panel">
                <div class="panel-title">车辆品牌召回数 Top 10</div>
                <div class="chart-content">
                    <canvas id="top10MakesChart"></canvas>
                </div>
            </div>
            <div class="panel" id="type-panel">
                <div class="panel-title">召回类型分布</div>
                <div class="chart-content">
                    <canvas id="recallsByTypeChart"></canvas>
                </div>
            </div>
            <div class="panel" id="component-panel">
                <div class="panel-title">召回组件 Top 5</div>
                 <div class="chart-content">
                    <canvas id="top5RecallComponentsChart"></canvas>
                </div>
            </div>
             <div class="panel" id="initiative-panel">
                <div class="panel-title">召回制造商</div>
                 <div class="chart-content">
                    <canvas id="recallsByRecallManufacturerChart"></canvas>
                </div>
            </div>
            <div class="panel" id="model-panel">
                <div class="panel-title">热门召回车型 Top 10</div>
                <div class="table-content">
                    <table class="data-table" id="top10ModelsTable"></table>
                </div>
            </div>
        </main>
    </div>

    <!-- Progress Modal -->
    <div id="progress-modal" class="modal">
        <div class="modal-content">
            <h2>数据刷新中...</h2>
            <div id="progress-bar-container"><div id="progress-bar">0%</div></div>
            <div id="progress-log"></div>
        </div>
    </div>
    
    <script>
        // Chart.js全局配置
        Chart.defaults.color = 'rgba(224, 224, 224, 0.8)';
        Chart.defaults.borderColor = 'rgba(36, 255, 236, 0.1)';

        const wsPort = 8081;
        let charts = {};

        // --- 时钟 ---
        function updateClock() {
            document.getElementById('live-clock').textContent = new Date().toLocaleString('zh-CN', { hour12: false });
        }
        setInterval(updateClock, 1000);

        // --- WebSocket处理, Refresh按钮逻辑 (与之前类似，此处省略以保持简洁) ---
        // ... (WebSocket and Refresh Button logic remains the same) ...

        // --- 绘图主函数 ---
        function drawAllCharts(data) {
             Object.values(charts).forEach(chart => chart.destroy());
             
             updateKPI(data.totalEstimatedUnits);
             drawBarChart(
                'top10Manufacturers', 'top10ManufacturersChart', 
                '召回次数', data.top10Manufacturers, 'bar', false, '制造商', '召回次数'
             );
             drawBarChart(
                'top10Makes', 'top10MakesChart', 
                '召回次数', data.top10Makes, 'bar', true, '召回次数', '车辆品牌'
             );
             drawDoughnutOrPie(
                'recallsByType', 'recallsByTypeChart', 
                '召回类型', data.recallsByType, 'doughnut'
             );
             drawDoughnutOrPie(
                'top5Components', 'top5RecallComponentsChart', 
                '召回组件', data.top5RecallComponents, 'pie'
             );
             drawInitiativeChart(
                'recallsByRecallManufacturer', 'recallsByRecallManufacturerChart',
                '制造商统计', data.recallsByRecallManufacturer
             );
             drawModelsTable('top10ModelsTable', data.top10Models);
        }

        // --- 具体绘图/更新函数 ---
        function updateKPI(value) {
            document.getElementById('kpi-total-units').textContent = value.toLocaleString('zh-CN');
        }

        function drawBarChart(chartId, canvasId, datasetLabel, rawData, type, yAxis = false, xAxisTitle = '', yAxisTitle = '') {
             const labels = Object.keys(rawData);
             const values = Object.values(rawData);
             charts[chartId] = new Chart(document.getElementById(canvasId), {
                type: type,
                data: {
                    labels: labels,
                    datasets: [{ label: datasetLabel, data: values, backgroundColor: 'rgba(36, 255, 236, 0.6)', borderColor: 'rgba(36, 255, 236, 1)' }]
                },
                options: { 
                    indexAxis: yAxis ? 'y' : 'x', 
                    responsive: true, 
                    maintainAspectRatio: false,
                    plugins: {
                        legend: { display: false }
                    },
                    scales: {
                        x: {
                            title: { display: !!xAxisTitle, text: xAxisTitle }
                        },
                        y: {
                            title: { display: !!yAxisTitle, text: yAxisTitle }
                        }
                    }
                }
            });
        }

        function drawDoughnutOrPie(chartId, canvasId, label, rawData, type) {
            const backgroundColors = [
                'rgba(36, 255, 236, 0.7)', 'rgba(233, 69, 96, 0.7)', 'rgba(255, 206, 86, 0.7)',
                'rgba(153, 102, 255, 0.7)', 'rgba(255, 159, 64, 0.7)', 'rgba(75, 192, 192, 0.7)'
            ];
            charts[chartId] = new Chart(document.getElementById(canvasId), {
                type: type,
                data: {
                    labels: Object.keys(rawData),
                    datasets: [{ label: label, data: Object.values(rawData), backgroundColor: backgroundColors }]
                },
                options: { responsive: true, maintainAspectRatio: false }
            });
        }
        
        function drawInitiativeChart(chartId, canvasId, datasetLabel, rawData) {
             const backgroundColors = [
                'rgba(36, 255, 236, 0.6)',
                'rgba(233, 69, 96, 0.6)',
                'rgba(255, 206, 86, 0.6)'
            ];
             charts[chartId] = new Chart(document.getElementById(canvasId), {
                type: 'bar',
                data: {
                    labels: Object.keys(rawData),
                    datasets: [{ label: datasetLabel, data: Object.values(rawData), backgroundColor: backgroundColors }]
                },
                options: {
                    indexAxis: 'y',
                    responsive: true, 
                    maintainAspectRatio: false,
                    plugins: {
                        legend: { display: false }
                    },
                    scales: {
                        x: {
                            title: { display: true, text: '召回次数' }
                        }
                    }
                }
            });
        }

        function drawModelsTable(tableId, rawData) {
            const table = document.getElementById(tableId);
            let html = `<thead><tr><th>排名</th><th>车型</th><th>召回次数</th></tr></thead><tbody>`;
            let rank = 1;
            for (const [model, count] of Object.entries(rawData)) {
                html += `<tr><td>${rank++}</td><td>${model}</td><td>${count.toLocaleString()}</td></tr>`;
            }
            html += `</tbody>`;
            table.innerHTML = html;
        }

        // --- 初始化 & WebSocket (与之前版本相同) ---
        // For brevity, the full WebSocket and initial load logic is omitted, but it's identical to the previous version.
        document.addEventListener('DOMContentLoaded', () => {
            updateClock();
            fetchAndDrawCharts();
            setupWebSocket();
        });

        // The full WebSocket and refresh button logic is required for a complete file.
        // It's just shortened here for the edit proposal.

        function fetchAndDrawCharts() {
            fetch('/analysis_results.json')
                .then(response => response.json())
                .then(data => drawAllCharts(data))
                .catch(error => console.error('加载分析数据时出错:', error));
        }

        function setupWebSocket() {
            const socket = new WebSocket(`ws://${window.location.hostname}:${wsPort}`);
            socket.onopen = () => console.log('WebSocket连接已建立。');
            socket.onclose = () => console.log('WebSocket连接已关闭。');
            socket.onerror = (error) => console.error('WebSocket错误:', error);
            socket.onmessage = (event) => {
                const data = JSON.parse(event.data);
                const logContainer = document.getElementById('progress-log');
                if (data.type === 'log' || data.type === 'error') {
                    const p = document.createElement('p');
                    p.textContent = `[${new Date().toLocaleTimeString()}] ${data.message}`;
                    if(data.type === 'error') p.classList.add('log-error');
                    logContainer.appendChild(p);
                    logContainer.scrollTop = logContainer.scrollHeight;
                }
                if (data.progress >= 0) {
                    const progressBar = document.getElementById('progress-bar');
                    progressBar.style.width = data.progress + '%';
                    progressBar.textContent = data.progress + '%';
                }
                if (data.progress === 100) {
                    setTimeout(() => {
                        document.getElementById('progress-modal').style.display = 'none';
                        fetchAndDrawCharts();
                    }, 1500);
                }
            };
        }
        document.getElementById('refresh-btn').addEventListener('click', () => {
            if (confirm('系统将重新导入全部数据并进行分析，这将非常耗时。确定要继续吗？')) {
                const modal = document.getElementById('progress-modal');
                document.getElementById('progress-log').innerHTML = '';
                const progressBar = document.getElementById('progress-bar');
                progressBar.style.width = '0%';
                progressBar.textContent = '0%';
                modal.style.display = 'block';
                fetch('/api/refresh').catch(err => console.error("刷新请求失败:", err));
            }
        });
    </script>
</body>
</html> 